#ifndef HPP_THREADASSERT
#define HPP_THREADASSERT

#include <iostream>

#ifdef NDEBUG

#define THREADWARNER(obj)
#define SCOPED_TWCHECK(obj)
#define TWCHECK(obj)

#else

#define THREADWARNER(obj)   ThreadCollisionWarner _##obj;
#define SCOPED_TWCHECK(obj) ThreadCollisionWarner::ScopedCheck scoped_check_##obj(_##obj);
#define TWCHECK(obj)        ThreadCollisionWarner::Check check_##obj(_##obj);

#endif

class ThreadCollisionWarner {
public:
    ThreadCollisionWarner()
    :theActiveThread(0)
    { }

    ~ThreadCollisionWarner() { }

    class Check {
        public:
            Check(ThreadCollisionWarner& aTCW)
            :theWarner(aTCW)
            { theWarner.enter_self(); }

            ~Check() { }

        private:
            ThreadCollisionWarner& theWarner;
    };

    class ScopedCheck {
        public:
            ScopedCheck(ThreadCollisionWarner& aTCW)
            :theWarner(aTCW)
            { theWarner.enter(); }

            ~ScopedCheck() { theWarner.leave(); }

        private:
            ThreadCollisionWarner& theWarner;
    };

private:

    void enter_self() { 

        //If the active thread is 0 then I'll write the current thread ID
        //if two or more threads arrive here only one will success to 
        //write on theActiveThread the current thread ID
        if (! __sync_bool_compare_and_swap(&theActiveThread, 0, pthread_self())) { 

            //Last chance! may be is the thread itself calling from a critical section
            //another critical section
            if (!__sync_bool_compare_and_swap(&theActiveThread, pthread_self(), theActiveThread)) {
                std::cerr << "Thread Collision" << std::endl;
                assert(0);
            }
        }
    }

    void enter() { 
        if (!__sync_bool_compare_and_swap(&theActiveThread, 0, pthread_self())) {
            //gotcha! another thread is trying to use the same class
            std::cerr << "Thread Collision" << std::endl;
            assert(0);
        }
    }

    void leave() { 
        __sync_fetch_and_xor(&theActiveThread, theActiveThread);
    }

    pthread_t theActiveThread;
};

#endif

